﻿﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Xml;
using Quexcel.OpenXML.Exceptions;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using System.IO.Packaging;
using System.IO;
using Quexcel.OpenXML.Transaction;
using Quexcel.OpenXML.Interfaces;
using Quexcel.OpenXML.Excel.Factory;

namespace Quexcel.OpenXML
{
    public class ExcelConnection : IDisposable
    {
        private ConnectionState connectionState;

        internal Package xlPackage;
        internal XmlDocument workbookDoc;
        internal PackagePart documentPart;
        internal PackagePart sheetPart;
        internal XmlNamespaceManager nsManager;
        internal Quexcel.OpenXML.Excel.Workbook.Workbook workbook;

        internal readonly string documentRelationshipType;
        internal readonly string worksheetSchema;

        private TransactionManager transactionManager;

        #region Properties
        public string ConnectionString { get; set; }
        public ConnectionState State { get { return connectionState; } }
        #endregion

        #region Ctor
        public ExcelConnection() : this(null) { }

        public ExcelConnection(string connectionString)
        {
            connectionState = ConnectionState.Closed;
            ConnectionString = connectionString;
            transactionManager = new TransactionManager();

            documentRelationshipType = System.Configuration.ConfigurationSettings.AppSettings["documentRelationshipType"];
            worksheetSchema = System.Configuration.ConfigurationSettings.AppSettings["worksheetSchema"];
        }
        #endregion

        public void Open()
        {
            try
            {
                connectionState = ConnectionState.Connecting;
                if (String.IsNullOrEmpty(ConnectionString))
                    throw new Exception("Invalid connection string!");
                else
                {
                    ConnectionStringLoader csl = new ConnectionStringLoader(ConnectionString);
                    if (LoadConnectionToExcel(csl[ConnectionStringFields.Database], csl[ConnectionStringFields.InitialCatalog]))
                        connectionState = ConnectionState.Open;
                    else
                        connectionState = ConnectionState.Broken;
                }
            }
            catch (Exception ex)
            {
                connectionState = ConnectionState.Broken;
                InvalidConnectionStateException ice = new InvalidConnectionStateException(connectionState, ex.Message);
                throw ice;
            }
        }

        public void Close()
        {
            try
            {
                connectionState = ConnectionState.Closed;
                xlPackage.Close();
                transactionManager.CloseAll();
            }
            finally
            {
                workbookDoc = null;
                documentPart = null;
                sheetPart = null;

                connectionState = ConnectionState.Closed;
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            this.Close();
        }

        #endregion

        private bool LoadConnectionToExcel(string fileName, string sheetName)
        {
            bool returnValue = false;
            Uri documentUri = null;

            xlPackage = Package.Open(fileName, FileMode.Open, FileAccess.ReadWrite);
            foreach (System.IO.Packaging.PackageRelationship relationship
              in xlPackage.GetRelationshipsByType(documentRelationshipType))
            {
                documentUri = PackUriHelper.ResolvePartUri(new Uri("/",
                  UriKind.Relative), relationship.TargetUri);
                documentPart = xlPackage.GetPart(documentUri);

                break;
            }

            if (documentPart != null)
            {
                workbook = new Quexcel.OpenXML.Excel.Workbook.Workbook(this, documentPart);
                returnValue = true;
            }
            return returnValue;
        }

        internal long AddTransaction(ExcelTransaction transaction)
        {
            return this.transactionManager.Add(transaction);
        }

        internal IWorkbookReader GetWorkbookReader()
        {
            return ReaderWriterBuilder.GetWorkbookReader(documentPart, xlPackage);
        }

        internal IWorkbookWriter GetWorkbookWriter()
        {
            return ReaderWriterBuilder.GetWorkbookWriter(documentPart, xlPackage);
        }
    }
}
